package models

import dao._

// Contantes de estado
object Status extends Enumeration {
  type Status = Value
  val ACTIVE, PENDING, BLOCKED = Value
}


// Constantes de idioma
object Language extends Enumeration {
    type Language = Value
    val SPANISH = Value("Español")
    val ENGLISH = Value("English")
    val ITALIAN = Value("Italiano")
    val PORTUGUESE = Value("Português")
    val FRENCH = Value("Français")
    val GERMAN = Value("Deutsch")
}

// Constantes de adjuntos en los posts 
object MediaKind extends Enumeration {
    type MediaKind = Value
    val NONE = Value("none")
    val IMAGE = Value("image")
    val VIDEO = Value("video")
    val SOUND = Value("sound")
    val LINK = Value("link")
}


import Status._
import MediaKind._
import Language._

/** Entidad que representa las publiciones hechas por los usuarios. Igualdad y hashing basados en el nombre del autor
 * y la fecha de creación.
 * Requiere una instancia de Session como parámetro implícito.
 */
class Post(
  var title:String, var content:String, val author:User, val created:java.util.Date,
  var modified:java.util.Date, var year:Short, val location:Post.Location, var category:Post.Category,
  var resource:Post.Media, var status:Status) {
  
  override def hashCode:Int = author.hashCode() + created.hashCode()
  
  override def equals(a: Any):Boolean = {
    if(a.isInstanceOf[Post]) {
      (a.asInstanceOf[Post].created == created) && (a.asInstanceOf[Post].author == author)
    } else {
      false
    }
  }
  
  override def toString(): String = {
    "Post(" + title + ", " + content + "," + author.toString + ")"
  }
  /**
   * Devuelve los comentarios asociados a esta publicación
   * @param s (implicit) Sesión de la BD sobre la cual trabajar en el DAO.
   */
  def getComments()(implicit s:Session):Option[List[Comment]] = CommentDAO.findByPost(this)
  
  /**
   * Devuelve el id de esta instancia en la BD
   */
  def id()(implicit s:Session) = PostDAO.getID(this)
  
  def update()(implicit s:Session) = PostDAO.update(this)
  
}

/**
 * Companion Object de la clase Post
 */
object Post{
  type Category = Category.Value
  
  /** Coordenadas de la publicación */
  case class Location(latitude:Double, longitude:Double)
  
  /** Archivo de referencia adjunto a la publicación */ 
  case class Media(kind:MediaKind, url:String)
  
  /** Constantes de categoría */
  object Category extends Enumeration {
    type Category = Value
    val ARTES_ENTRETENCION = Value("ARTES\nENTRETENCIÓN")
    val FIESTAS_TRADICIONES = Value("FIESTAS\nTRADICIONES")
    val DEPORTES_PASATIEMPOS = Value("DEPORTES\nPASATIEMPOS")
    val FENOMENOS_NATURALES = Value("FENÓMENOS\nNATURALES")
    val RELIGION_FILOSOFIA = Value("RELIGIÓN\nFILOSOFÍA")
    val AMISTAD_VIDA_FAMILIAR = Value("AMISTAD\nVIDA FAMILIAR")
    val BARES_RESTORANES = Value("BARES\nRESTORANES")
    val ECONOMIA_NEGOCIOS = Value("ECONOMÍA\nNEGOCIOS")
    val ANIMALES_MASCOTAS = Value("ANIMALES\nMASCOTAS")
    val EDUCACION_COMUNICACIONES = Value("EDUCACIÓN\nCOMUNICACIONES")
    val JUDICIAL_POLITICA = Value("JUDICIAL\nPOLÍTICA")
    val UFOLOGIA_PARANORMAL = Value("UFOLOGÍA\nPARANORMAL")
    val OTRAS = Value("OTRAS\nCATEGORÍAS")
  }
}


/**
 * User
 * Entidad que modela los usuarios de la aplicación. Sólo los detalles de uso inmediato se mantienen en el primer nivel
 * los demás son delegados a clases miembros, tal que al recuperarlos de la BD sean instanciados a pedido (lazy loading,
 * transparent activation). Hashing e igualdad basados en el nombre del usuario */

import scala.collection.immutable.HashSet
import java.util.Calendar
class User(
  val name:String, var fullname:String, var password:String, var photo:String,
  var pref:Option[User.Preferences], var det:Option[User.Details], var friends:HashSet[User],
  var faves:HashSet[Post], var messages:List[Message], val registerDate:java.util.Date, var language:Language, var status:Status) {
  
  override def hashCode:Int = name.hashCode()

  override def equals(a: Any):Boolean = {
    if(a.isInstanceOf[User]) {
      (a.asInstanceOf[User].name == name)
    } else {
      false
    }
  }
  
  override def toString(): String = "User(" + name + ", " + fullname + "," + status + "})"
  
  /** Añadir un usuario a la lista de contactos
   * @param u Usuario a seguir.
   */
  def follow(u: User)(implicit s:Session){ friends += u;update() }
  
  /** Eliminar un usuario de la lista de contactos
   * @param u Usuario a dejar de seguir,
   */
  def unfollow(u: User)(implicit s:Session){ friends -= u;update() }
  
  /** Marcar una publicación como destacada
   * @param p Publicación a destacar.
   */
  def fave(p:Post)(implicit s:Session){ faves += p; update() }
  
  /** Eliminar una publicación
   * @param p Publicación a desmarcar como destacada.
   */
  def unfave(p:Post)(implicit s:Session){ faves -= p; update() }
  
  /** Sincronizar la instancia en memoria con la persistida en la BD.
   */
  def update()(implicit s:Session){ UserDAO.update(this) }
  
  /** Crear una publicación nueva asociada al usuario actual.
   * @param title Título de la publicación
   * @param content Texto de la publicación
   * @param year año asociado
   * @param location objeto con las coordenadas
   * @param resource archivo adjunto a la publicación
   * @param category categoría
   * @return la publicación recién creada y persistida en la BD.
   */
  def publish(title:String, content:String, year:Short, location:Post.Location, 
      resource: Post.Media = Post.Media(MediaKind.NONE, ""), category:Post.Category = Post.Category.OTRAS)
      (implicit s:Session):Option[Post] = PostDAO.createAndRef(title, content, this, new java.util.Date,
          new java.util.Date, year, location, category, resource, Status.ACTIVE)
  
  /** Crear un comentario del usuario actual en la publicación indicada
   * @param content texto del comentario
   * @param post publicación asociada
   * @return el comentario recién creado y persistido en la BD
   */
  def comment(content:String, post:Post)(implicit s:Session):Option[Comment] = {
    CommentDAO.createAndRef(this, post, new java.util.Date, content, Status.ACTIVE)
  }
  
  /** Enviar un mensaje al usuario seleccionado
   * @param u usuario al que mandar el mensaje
   * @param title encabezado del mensaje
   * @param m contenido del mensaje
   * @return true si el mensaje fue enviado con éxito
   */
  def sendMessage(u:User, title:String, m:String)(implicit s:Session):Boolean = {
    MessageDAO.createAndRef(this, u, title, m) match {
      case Some(msg)    => u.messages ::= msg; u.update(); true
      case None         => false
    }
    
  }
  
  /** Devolver las publicaciones asociadas a este usuario
   * @return lista de publicaciones
   */
  def posts()(implicit s:Session) = {
    PostDAO.findByAuthor(this) //FIXME: Request not limited
  }
  
  /** Devolver los comentaios asociados a este usuario
   * @return lista de comentarios
   */
  def comments()(implicit s:Session) = {
    CommentDAO.findByUser(this)
  }
  
  /** Calcular la edad del usuario a la fecha.
   * @return edad del usuario (0 si no ha indicado su cumpleaños)
   */
  def age:Int = { //FIXME: Return type Option[Byte]
    val today = new java.util.Date()
    det.map( today.getYear - _.birthday.getYear ).getOrElse(0)
  }
  
}

/** Companion Object de la clase User
 */
object User {
 
  case class Preferences(var fontSize:Byte, var themeName:String)
  
  case class Details (var country: String, var livedIn:String, var experience:String, var hobbies:String,
    var toKnow:String, var bestInLife:String, birthday: java.util.Date)
  
  /** Generar una instancia estándar de User.Preferences en caso de ser necesario.
   * @return una instancia de User.Preferences
   */
  def getDefaultPreferences:User.Preferences = {
    User.Preferences(12, "default")
  }

}

/** Entidad que modela los comentarios dejados en publicaciones. Hashing e igualdad basados en la fecha de creación
 * del comentario.
 */
class Comment(val author:User, val post:Post, val created:java.util.Date, var content:String, var status:Status) {
  
  override def hashCode():Int = created.hashCode()

  override def equals(a: Any):Boolean = {
    if(a.isInstanceOf[Post]) {
      (a.asInstanceOf[Comment].created == created)  
    } else {
      false
    }
  }
  
  override def toString(): String = {
    "Comment(" + author.name + ", " + post.title + "," + content + ")"
  }
  
  /** Obtener el id asignado a este comentario en la BD
   * @return el id de esta instancia.
   */
  def id()(implicit s:Session) = {
    CommentDAO.getID(this)
  }
  
}

/** Entidad que modela los mensajes enviados entre usuarios. Hashing e igualdad basados en la fecha de envío. 
 */
class Message(val author:User, val destination:User, val title:String, val content:String, val created:java.util.Date) {
  
  override def hashCode:Int = author.hashCode +  created.hashCode

  override def equals(a: Any):Boolean = {
    if(a.isInstanceOf[Message]) {
      (a.asInstanceOf[Message].author == author) && (a.asInstanceOf[Message].created == created)  
    } else {
      false
    }
  }
  
  override def toString(): String = {
    "Comment(" + author.name + ", " + title + "," + content + ")"
  }
  
  /** Encontrar el id asignado a esta instancia en la BD
   * @return id asignado a esta instancia en la BD
   */
  def id()(implicit s:Session) = {
    MessageDAO.getID(this)
  }
  
}
